package com.lagou.edu.factory;

import com.lagou.edu.anno.AnnotatedBeanDefinition;
import com.lagou.edu.anno.MyAutowired;
import com.lagou.edu.anno.MyQualifier;
import com.lagou.edu.anno.MyTransactional;
import com.lagou.edu.utils.StringUtil;

import java.io.File;
import java.lang.reflect.Field;
import java.lang.reflect.Type;
import java.net.URL;
import java.util.*;

public class AnnotationConfigApplicationContext {

    private ClassLoader classLoader;
    private List<String> nameList;

    private List<AnnotatedBeanDefinition> definitionList = new ArrayList<>();
    /**
     * 类继承关系
     */
    private Map<String, List<AnnotatedBeanDefinition>> extRelate = new HashMap<>();
    /**
     * 完整对象缓存map
     */
    private Map<String, Object> singletonObjects = new HashMap<>();

    /**
     * 未完成对象缓存map 二级缓存
     */
    private Map<String, Object> earlyObjects = new HashMap<>();


    public AnnotationConfigApplicationContext() {
        nameList = new ArrayList<>();
        classLoader = getClass().getClassLoader();

    }

    public AnnotationConfigApplicationContext(String... scanPackage) {
        this();
        scan(scanPackage);
        refresh();
    }

    /**
     * 扫描包和注解
     * 1. 扫描包下所有的class文件
     * 2. 依次判断是否有注解@Component
     * 3. 存储需要实例化的类定义信息list（接口不需要）
     *
     * @param scanPackage
     */
    private void scan(String... scanPackage) {
        for (String basePackage : scanPackage) {
            doScan(basePackage);
        }

    }

    /**
     * 刷新Bean定义到map中
     * 1. 遍历类定义list
     * 2. 通过反射加载对象，并存储到map中，key为注解的值，未设置则为类名的驼峰命名
     * 3. 根据注解@Autowired设置属性对象（循环依赖问题，从map中取）
     * 4. 扫描@Transactional注解创建动态代理对象（JDK&CGLIB），创建声明式事务
     * 5. 多态属性注入
     */
    private void refresh() {
        System.out.println("刷新 ");
        // 遍历
        for (AnnotatedBeanDefinition beanDefinition : definitionList) {
            // String beanId = beanDefinition.getBeanId();

            // 实例化bean对象
            Object bean = doCreatBean(beanDefinition);


        }
        System.out.println(singletonObjects);

    }

    /**
     * 真正创建实例化bean的方法
     *
     * @param beanDefinition 类定义器
     * @return
     */
    private Object doCreatBean(AnnotatedBeanDefinition beanDefinition) {
        Object obj = null;
        String beanId = beanDefinition.getBeanId();
        obj = singletonObjects.get(beanId);
        if (obj != null) {
            return obj;
        }
        try {
            // 1. 实例化bean
            obj = earlyObjects.get(beanId);
            Class beanClass = beanDefinition.getBeanClass();
            obj = obj == null ? beanClass.newInstance() : obj;

            // 2. 提前暴露在容器中
            earlyObjects.put(beanId, obj);


            // 3. 属性注入
            Field[] declaredFields = beanClass.getDeclaredFields();
            for (Field field : declaredFields) {
                if (field.isAnnotationPresent(MyAutowired.class)) {
                    doInjectProperties(obj, field);
                }
            }

            // 4. 增强操作：扫描其他注解，如@MyTransactional
            if (beanClass.isAnnotationPresent(MyTransactional.class) && beanClass != ProxyFactory.class) {
                ProxyFactory proxyFactory = (ProxyFactory) doCreatBean(new AnnotatedBeanDefinition(ProxyFactory.class));
                // 选择动态代理
                obj = beanClass.getInterfaces().length > 0 ? proxyFactory.getJdkProxy(obj) : proxyFactory.getCglibProxy(obj);

            }


        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException("无法创建类：" + beanDefinition.getBeanClass().getName());
        }
        // 刷新缓存
        if (obj != null) {
            earlyObjects.remove(beanId);
            singletonObjects.put(beanId, obj);
        }

        return obj;
    }

    /**
     * 属性注入
     *
     * @param obj
     * @param field
     */
    private void doInjectProperties(Object obj, Field field) {
        Class<?> fieldDeclaringClass = field.getType();
        // 选择注入的实体类，接口需要处理
        AnnotatedBeanDefinition propertiesBeanDefinition = new AnnotatedBeanDefinition(fieldDeclaringClass);
        if (fieldDeclaringClass.isInterface()) {
            // 子类集合
            List<AnnotatedBeanDefinition> subClassList = extRelate.get(propertiesBeanDefinition.getBeanId());
            if (subClassList == null || subClassList.size() < 1) {
                throw new RuntimeException(String.format("没有扫描到 %s 的实现类，无法加载属性", field.getName()));
            } else if (subClassList.size() > 1) {
                // 有多个实现类，则遍历根据beanId选择
                MyQualifier qualifier = field.getAnnotation(MyQualifier.class);
                if(qualifier == null){
                    throw new RuntimeException(String.format("扫描到 %s 的实现类有多个，但没有指定需要哪一个，无法加载属性", field.getName()));
                }
                propertiesBeanDefinition = subClassList.stream().filter(e -> e.getBeanId().equals(qualifier.value())).findFirst().orElse(null);
                if(propertiesBeanDefinition == null){
                    throw new RuntimeException(String.format("没有beanId： %s 的实现类，无法加载属性", qualifier.value()));
                }
            } else {
                propertiesBeanDefinition = subClassList.get(0);
            }
        }
        // 1 先从一级缓存中找完整的bean
        String beanId = propertiesBeanDefinition.getBeanId();
        Object value = singletonObjects.get(beanId);

        // 2 找不到就在二级缓存中找正在构建的，
        value = value == null ? earlyObjects.get(beanId) : value;
        // 3 再没有就最后创建一个
        value = value == null ? doCreatBean(propertiesBeanDefinition) : value;
        // 没有则抛异常
        if (value == null) {
            throw new RuntimeException("无法创建类：" + propertiesBeanDefinition.getBeanClass().getName());
        }

        // 4 反射注入属性
        field.setAccessible(true);
        try {
            field.set(obj, value);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
            throw new RuntimeException("无法设置属性：" + field.getName());
        }

    }

    /**
     * doScan方法，扫描并加载类定义器（尚未实例化的）
     *
     * @param basePackage 扫描基础包 com.lagou.edu
     * @return
     */
    private void doScan(String basePackage) {
        // 将 [.] 转化为 [/]
        String splashPath = StringUtil.dotToSplash(basePackage);
        URL url = classLoader.getResource(splashPath);   // file:/D:/1lagoustudy/homework/modle2/code/lagou-transfer/target/classes/com/lagou/edu
        String filePath = StringUtil.getRootPath(url);   // /D:/1lagoustudy/homework/modle2/code/lagou-transfer/target/classes/com/lagou/edu
        // 包含 basePackage下所有第一层文件或文件夹名字 如：TransferServiceImpl.class
        List<String> names = readFromDirectory(filePath);

        for (String name : names) {
            if (name.endsWith(".class")) {
                String classPath = basePackage + "." + name;
                nameList.add(classPath);
                loadBeanDefinition(classPath);
            } else {
                doScan(basePackage + "." + name);
            }
        }

    }

    /**
     * 加载class，并定义BeanDefinition
     *
     * @param classPath
     */
    private void loadBeanDefinition(String classPath) {
        Class<?> loadClass = null;
        try {
            classPath = classPath.substring(0, classPath.lastIndexOf(".class"));
            loadClass = classLoader.loadClass(classPath);

        } catch (ClassNotFoundException e) {
            e.printStackTrace();
            throw new RuntimeException("类加载失败：" + classPath);
        }

        AnnotatedBeanDefinition beanDefinition = new AnnotatedBeanDefinition(loadClass);
        if (beanDefinition.isNeedInjection()) {
            definitionList.add(beanDefinition);
        }
        // 存在接口实现关系则缓存继承关系
        Class<?>[] classInterfaces = loadClass.getInterfaces();
        if (!loadClass.isAnnotation() && classInterfaces.length > 0) {
            for (Class<?> eachInterface : classInterfaces) {

                // 1 获取接口beanid
                AnnotatedBeanDefinition superClassDef = new AnnotatedBeanDefinition(eachInterface);
                String superBeanId = superClassDef.getBeanId();
                // 2 取缓存的继承关系
                // 3 没有则加入新的继承关系
                List<AnnotatedBeanDefinition> relateList = extRelate.computeIfAbsent(superBeanId, k -> new ArrayList<>());
                // 4 放入缓存
                relateList.add(beanDefinition);
            }
        }

    }


    /**
     * 获取路径下第一层文件名list
     *
     * @param path 基础路径
     * @return 文件名集合
     */
    private List<String> readFromDirectory(String path) {
        File file = new File(path);
        String[] names = file.list();

        if (null == names) {
            return null;
        }

        return Arrays.asList(names);
    }


    /**
     * 获取Bean对象
     *
     * @param beanId bean对象Id
     * @return
     */
    public Object getBean(String beanId) {
        return singletonObjects.get(beanId);
    }

    /**
     * 获取Bean对象
     *
     * @return
     */
    public <T> T getBean(Class<T> beanClass) {
        AnnotatedBeanDefinition beanDefinition = new AnnotatedBeanDefinition(beanClass);
        String beanId = beanDefinition.getBeanId();
        return (T) singletonObjects.get(beanId);
    }
}
